// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

import (
	"internal/goarch"
	"runtime/internal/atomic"
	"runtime/internal/sys"
	"unsafe"
)

// 我们有两种不同的延迟方式。旧的方法是在执行DEBER语句时创建一个
// DEBER记录，并将其添加到
// DEBER链中。此链由所有函数
// 出口处的延迟返回调用检查，以便运行适当的延迟调用。对于
// 开放编码延迟）。在这种情况下，我们只需在每个DEBER语句的点上将DEBER函数/arg信息存储到
// 循环中没有延迟语句的函数，使用了一种更便宜的方法（我们称之为
// 特定的堆栈槽中，并在位掩码中设置一个
// 位。在每个函数出口处，我们添加内联代码，根据堆栈上存储的位掩码和fn/arg信息，直接进行
// 适当的延迟调用。在panic/Goexit处理过程中，适当的延迟调用是
// 使用额外的funcdata信息进行的，该信息指示
// 包含位掩码和延迟fn/args的确切堆栈插槽。

// 检查以确保我们真的能引起恐慌。如果死机
// 是从运行时生成的，或者是从malloc内部生成的，那么将
// 转换为一个msg。
// pc应该是
// 触发此死机的编译器生成代码的程序计数器。
func panicCheck1(pc uintptr, msg string) {
	if goarch.IsWasm == 0 && hasPrefix(funcname(findfunc(pc)), "runtime.") {
		// 注意：wasm无法跟踪呼叫，因此我们无法获取原始呼叫方的pc。
		throw(msg)
	}
	// TODO:这是多余的吗？我们怎么可能在malloc 
	// 但不在运行时？运行时/内部/*，也许？
	gp := getg()
	if gp != nil && gp.m != nil && gp.m.mallocing != 0 {
		throw(msg)
	}
}

// 同上，但允许从运行时调用。
// 
// 对于运行时生成的任何死机，都必须使用此函数。sigmanic，因为它们总是由
// 运行时调用。
func panicCheck2(err string) {
	// 恐慌分配，为了避免递归malloc，将malloc期间的恐慌变成抛出。
	gp := getg()
	if gp != nil && gp.m != nil && gp.m.mallocing != 0 {
		throw(err)
	}
}

// 以下许多恐慌进入点在不同的运行时上下文中发生时会变成抛出。这些情况在运行时绝对不应该发生在
// 中，如果发生，则表明问题严重，用户代码不应该捕捉到
// 中的错误。
// 
// 由
// 编译器为越界索引表达式、
// 越界切片表达式、除零和负移位生成的代码调用panic{Index，Slice，divide，shift}函数。
// 当信号出现时，信号处理程序将调用panicdivide（再次）、panicoverflow、panicfloat和panicmem 
// 函数，以指示相应的问题。
// 
// 由于从来没有直接调用过panic{Index，Slice，shift}，而
// 由于运行时包永远不应该有一个越界的Slice 
// 或数组引用或负移位，如果我们看到从
// 运行时包调用的那些函数，我们会把panic变成抛出。这将转储整个运行时堆栈，以便于调试。
// 
// 信号处理程序调用的入口点将从
// 运行时调用。sigmanic，所以我们不能禁止从运行时调用
// 这些（它们看起来总是从运行时调用的）。
// 因此，对于这些，我们只需检查运行时条件是否明显不好。
// 
// panic{Index，Slice}函数在下面的汇编和尾部调用中实现。这样我们就可以使用
// 一种空间最小的寄存器调用约定。

// s[x]比较失败，0<=x<y（y==len（s））
func goPanicIndex(x int, y int) {
	panicCheck1(getcallerpc(), "index out of range")
	panic(boundsError{x: int64(x), signed: true, y: y, code: boundsIndex})
}
func goPanicIndexU(x uint, y int) {
	panicCheck1(getcallerpc(), "index out of range")
	panic(boundsError{x: int64(x), signed: false, y: y, code: boundsIndex})
}

// s[：x]比较失败，0<=x<=y（y==len（s）或cap（s））
func goPanicSliceAlen(x int, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAlen})
}
func goPanicSliceAlenU(x uint, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAlen})
}
func goPanicSliceAcap(x int, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceAcap})
}
func goPanicSliceAcapU(x uint, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceAcap})
}

// s[x:y]比较失败，0<=x<=y 
func goPanicSliceB(x int, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSliceB})
}
func goPanicSliceBU(x uint, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSliceB})
}

// s[：x]比较失败，0<=x<=y（y==len（s）或cap（s））
func goPanicSlice3Alen(x int, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Alen})
}
func goPanicSlice3AlenU(x uint, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Alen})
}
func goPanicSlice3Acap(x int, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3Acap})
}
func goPanicSlice3AcapU(x uint, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3Acap})
}

// s[:x:y]比较失败，0<=x<=y 
func goPanicSlice3B(x int, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3B})
}
func goPanicSlice3BU(x uint, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3B})
}

// s[x:y:]比较失败，0<=x<=y 
func goPanicSlice3C(x int, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: true, y: y, code: boundsSlice3C})
}
func goPanicSlice3CU(x uint, y int) {
	panicCheck1(getcallerpc(), "slice bounds out of range")
	panic(boundsError{x: int64(x), signed: false, y: y, code: boundsSlice3C})
}

// 转换失败（*[x]T）s，0<=x<=y，x==cap 
func goPanicSliceConvert(x int, y int) {
	panicCheck1(getcallerpc(), "slice length too short to convert to pointer to array")
	panic(boundsError{x: int64(x), signed: true, y: y, code: boundsConvert})
}

// 在汇编中实现，因为它们在寄存器中使用参数。
// 在此处声明，以将其标记为ABIInternal。
func panicIndex(x int, y int)
func panicIndexU(x uint, y int)
func panicSliceAlen(x int, y int)
func panicSliceAlenU(x uint, y int)
func panicSliceAcap(x int, y int)
func panicSliceAcapU(x uint, y int)
func panicSliceB(x int, y int)
func panicSliceBU(x uint, y int)
func panicSlice3Alen(x int, y int)
func panicSlice3AlenU(x uint, y int)
func panicSlice3Acap(x int, y int)
func panicSlice3AcapU(x uint, y int)
func panicSlice3B(x int, y int)
func panicSlice3BU(x uint, y int)
func panicSlice3C(x int, y int)
func panicSlice3CU(x uint, y int)
func panicSliceConvert(x int, y int)

var shiftError = error(errorString("negative shift amount"))

func panicshift() {
	panicCheck1(getcallerpc(), "negative shift amount")
	panic(shiftError)
}

var divideError = error(errorString("integer divide by zero"))

func panicdivide() {
	panicCheck2("integer divide by zero")
	panic(divideError)
}

var overflowError = error(errorString("integer overflow"))

func panicoverflow() {
	panicCheck2("integer overflow")
	panic(overflowError)
}

var floatError = error(errorString("floating point error"))

func panicfloat() {
	panicCheck2("floating point error")
	panic(floatError)
}

var memoryError = error(errorString("invalid memory address or nil pointer dereference"))

func panicmem() {
	panicCheck2("invalid memory address or nil pointer dereference")
	panic(memoryError)
}

func panicmemAddr(addr uintptr) {
	panicCheck2("invalid memory address or nil pointer dereference")
	panic(errorAddressString{msg: "invalid memory address or nil pointer dereference", addr: addr})
}

// 创建一个新的延迟函数fn，它没有参数和结果。
// 编译器将DEBER语句转换为对该语句的调用。
func deferproc(fn func()) {
	gp := getg()
	if gp.m.curg != gp {
		// 系统堆栈上的go代码不能延迟
		throw("defer on system stack")
	}

	d := newdefer()
	if d._panic != nil {
		throw("deferproc: d.panic != nil after newdefer")
	}
	d.link = gp._defer
	gp._defer = d
	d.fn = fn
	d.pc = getcallerpc()
	// 我们不能在调用getcallersp和
	// 将其存储到d.sp之间被抢占，因为getcallersp的结果是
	// uintptr堆栈指针。
	d.sp = getcallersp()

	// deferproc正常返回0。
	// 停止恐慌的延迟函数
	// 使deferproc返回1。
	// 编译器生成的代码始终
	// 检查返回值，并跳到
	// 如果deferproc返回，则函数结束！=0.
	return0()
	// 此处不能输入任何代码-C返回寄存器已设置
	// 且不能被删除。
}

// deferprocStack将一个新的延迟函数与堆栈上的延迟记录一起排队。
// 延迟记录必须初始化其fn字段。
// 所有其他字段都可以包含垃圾邮件。
// 由于堆栈上未初始化的指针字段，因此无法拆分。
// 
// go:nosplit 
func deferprocStack(d *_defer) {
	gp := getg()
	if gp.m.curg != gp {
		// 系统堆栈上的go代码不能延迟
		throw("defer on system stack")
	}
	// fn已设置。
	// 其他字段在进入deferprocStack时是垃圾字段，
	// 在此处初始化。
	d.started = false
	d.heap = false
	d.openDefer = false
	d.sp = getcallersp()
	d.pc = getcallerpc()
	d.framepc = 0
	d.varp = 0
	// 下面的行实现：
	// d.panic=nil 
	// d.fd=nil 
	// d.link=gp_推迟
	// gp_DEBER=d 
	// 但没有写入障碍。前三个是对
	// 堆栈的写入，因此它们不需要写屏障，而且
	// 是对未初始化内存的写入，因此它们不能使用写屏障。
	// 第四次写入不需要写屏障，因为我们
	// 显式标记所有延迟结构，所以我们不需要
	// 使用写屏障跟踪指向它们的指针。
	*(*uintptr)(unsafe.Pointer(&d._panic)) = 0
	*(*uintptr)(unsafe.Pointer(&d.fd)) = 0
	*(*uintptr)(unsafe.Pointer(&d.link)) = uintptr(unsafe.Pointer(gp._defer))
	*(*uintptr)(unsafe.Pointer(&gp._defer)) = uintptr(unsafe.Pointer(d))

	return0()
	// 此处不能输入任何代码-C返回寄存器已设置
	// 且不能被删除。
}

// 每个P都有一个延迟池。

// 分配一个延迟，通常使用per-P池。
// 每个延迟必须与freedefer一起释放。延迟尚未添加到任何延迟链中。
func newdefer() *_defer {
	var d *_defer
	mp := acquirem()
	pp := mp.p.ptr()
	if len(pp.deferpool) == 0 && sched.deferpool != nil {
		lock(&sched.deferlock)
		for len(pp.deferpool) < cap(pp.deferpool)/2 && sched.deferpool != nil {
			d := sched.deferpool
			sched.deferpool = d.link
			d.link = nil
			pp.deferpool = append(pp.deferpool, d)
		}
		unlock(&sched.deferlock)
	}
	if n := len(pp.deferpool); n > 0 {
		d = pp.deferpool[n-1]
		pp.deferpool[n-1] = nil
		pp.deferpool = pp.deferpool[:n-1]
	}
	releasem(mp)
	mp, pp = nil, nil

	if d == nil {
		// 分配新的延迟。
		d = new(_defer)
	}
	d.heap = true
	return d
}

// 释放给定延迟。
// 此呼叫后无法使用延迟。
// 
// 这是nosplit，因为传入的延迟处于危险状态。
// 它不在任何延迟列表中，因此堆栈复制不会调整其中的堆栈
// 指针（即d.link）。因此，如果我们要复制
// 堆栈，那么d可能会包含一个过时的指针。
// 
// go:nosplit 
func freedefer(d *_defer) {
	d.link = nil
	// 在这一点之后，我们可以复制堆栈。

	if d._panic != nil {
		freedeferpanic()
	}
	if d.fn != nil {
		freedeferfn()
	}
	if !d.heap {
		return
	}

	mp := acquirem()
	pp := mp.p.ptr()
	if len(pp.deferpool) == cap(pp.deferpool) {
		// 将本地缓存的一半传输到中央缓存。
		var first, last *_defer
		for len(pp.deferpool) > cap(pp.deferpool)/2 {
			n := len(pp.deferpool)
			d := pp.deferpool[n-1]
			pp.deferpool[n-1] = nil
			pp.deferpool = pp.deferpool[:n-1]
			if first == nil {
				first = d
			} else {
				last.link = d
			}
			last = d
		}
		lock(&sched.deferlock)
		last.link = sched.deferpool
		sched.deferpool = first
		unlock(&sched.deferlock)
	}

	*d = _defer{}

	pp.deferpool = append(pp.deferpool, d)

	releasem(mp)
	mp, pp = nil, nil
}

// 分离函数，以便可以拆分堆栈。
// 否则Windows会耗尽堆栈空间。
func freedeferpanic() {
	// /_必须在d与gp断开连接之前清除恐慌。
	throw("freedefer with d._panic != nil")
}

func freedeferfn() {
	// fn必须在d与gp解除链接之前清除。
	throw("freedefer with d.fn != nil")
}

// deferreturn为调用方的帧运行延迟函数。
// 编译器在调用defer的任何
// 函数的末尾插入对此函数的调用。
func deferreturn() {
	gp := getg()
	for {
		d := gp._defer
		if d == nil {
			return
		}
		sp := getcallersp()
		if d.sp != sp {
			return
		}
		if d.openDefer {
			done := runOpenDeferFrame(gp, d)
			if !done {
				throw("unfinished open-coded defers in deferreturn")
			}
			gp._defer = d.link
			freedefer(d)
			// 如果此帧使用开放延迟，则此
			// 必须是
			// 帧的唯一延迟记录，因此我们可以返回。
			return
		}

		fn := d.fn
		d.fn = nil
		gp._defer = d.link
		freedefer(d)
		fn()
	}
}

// Goexit终止了称之为goroutine的goroutine。没有其他goroutine受到影响。
// Goexit在终止goroutine之前运行所有延迟的调用。因为Goexit 
// 不是恐慌，所以这些延迟函数中的任何recover调用都将返回nil。
// 
// 从主goroutine调用Goexit终止该goroutine 
// 而不返回func main。由于func main尚未返回，
// 程序将继续执行其他goroutine。
// 如果所有其他Goroutine退出，程序将崩溃。
func Goexit() {
	// 运行当前goroutine的所有延迟函数。
	// 此代码类似于gopanic，请参阅实现
	// 以获取详细注释。
	gp := getg()

	// 为Goexit创建一个恐慌对象，这样我们就可以识别何时它可能被recover（）绕过。
	var p _panic
	p.goexit = true
	p.link = gp._panic
	gp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))

	addOneOpenDeferFrame(gp, getcallerpc(), unsafe.Pointer(getcallersp()))
	for {
		d := gp._defer
		if d == nil {
			break
		}
		if d.started {
			if d._panic != nil {
				d._panic.aborted = true
				d._panic = nil
			}
			if !d.openDefer {
				d.fn = nil
				gp._defer = d.link
				freedefer(d)
				continue
			}
		}
		d.started = true
		d._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
		if d.openDefer {
			done := runOpenDeferFrame(gp, d)
			if !done {
				// 我们应该始终运行帧中的所有延迟，
				// 因为没有与此
				// 延迟相关的可恢复恐慌。
				throw("unfinished open-coded defers in Goexit")
			}
			if p.aborted {
				// 由于我们当前的延迟引起了恐慌，可能
				// 已经被释放，只需重新扫描
				// 对于开放编码，再次从该帧延迟。
				addOneOpenDeferFrame(gp, getcallerpc(), unsafe.Pointer(getcallersp()))
			} else {
				addOneOpenDeferFrame(gp, 0, nil)
			}
		} else {
			// 将pc/sp保存在deferCallSave（）中，以便在必要时可以“恢复”到此
			// 循环。
			deferCallSave(&p, d.fn)
		}
		if p.aborted {
			// 我们在开始的延迟d中出现了一个递归恐慌，然后在延迟中进行了恢复，该延迟比d更靠后。在未完成的Goexit的情况下，
			// 我们强制恢复回到这个循环。d如果完成，
			// 已经被释放，所以只需继续
			// 立即到链上的下一个延迟。
			p.aborted = false
			continue
		}
		if gp._defer != d {
			throw("bad defer entry in Goexit")
		}
		d._panic = nil
		d.fn = nil
		gp._defer = d.link
		freedefer(d)
		// 注意：我们在这里忽略恢复，因为Goexit并不是恐慌的
	}
	goexit1()
}

// 在冻结世界之前调用所有错误和字符串方法。
// 因恐慌而崩溃时使用。
func preprintpanics(p *_panic) {
	defer func() {
		if recover() != nil {
			throw("panic while printing panic value")
		}
	}()
	for p != nil {
		switch v := p.arg.(type) {
		case error:
			p.arg = v.Error()
		case stringer:
			p.arg = v.String()
		}
		p = p.link
	}
}

// 打印所有当前活跃的恐慌。碰撞时使用。
// 只能在预印本之后调用。
func printpanics(p *_panic) {
	if p.link != nil {
		printpanics(p.link)
		if !p.link.goexit {
			print("\t")
		}
	}
	if p.goexit {
		return
	}
	print("panic: ")
	printany(p.arg)
	if p.recovered {
		print(" [recovered]")
	}
	print("\n")
}

// addOneOpenDeferFrame扫描堆栈（按gentraceback顺序，从内部帧到
// /外部帧），以查找具有开放编码延迟的第一帧（如果有）。如果它找到
// one，它会为该帧的延迟链添加一个条目。添加的条目
// 表示相关开放延迟帧中的所有延迟，并按照
// 顺序对任何非开放编码延迟进行排序。
// 
// addOneOpenDeferFrame在遇到
// 正在打开的延迟条目时停止（可能不添加新条目）。正在进行的打开延迟条目意味着
// 由于相关帧中的延迟，出现了新的恐慌。addOneOpenDeferFrame 
// 不会将打开的延迟条目添加到已启动条目之后，因为该已启动条目
// 仍需要完成，并且在该已启动
// 条目完成时将调用addOneOpenDeferFrame。gopanic（）中的延迟删除循环同样会在
// 进行中的延迟条目处停止。addOneOpenDeferFrame和延迟删除循环
// 确保不变量在堆栈的最上层没有打开的延迟项，而不是
// 正在进行的延迟，并且延迟删除循环保证从延迟链中删除所有未进行的打开延迟项。
// 
// 如果sp为非nil，addOneOpenDeferFrame将从sp指定的帧
// 开始堆栈扫描。如果sp为nil，它将使用当前延迟记录（
// 刚刚完成）中的sp。因此，它从刚刚完成的
// 帧继续堆栈扫描。它跳过延迟链中已经有（未进行中）
// 开放编码的延迟记录的任何帧。
// 
// 注意：延迟链的所有条目（包括这个新的开放编码条目）都有
// 如果在
// 运行延迟函数时堆栈移动，则它们的指针（包括sp）会得到适当调整。此外，可以安全地传入sp arg（即
// 调用getcallersp（）的直接结果），因为在堆栈复制期间，所有指针变量
// （包括参数）都会根据需要进行调整。
func addOneOpenDeferFrame(gp *g, pc uintptr, sp unsafe.Pointer) {
	var prevDefer *_defer
	if sp == nil {
		prevDefer = gp._defer
		pc = prevDefer.framepc
		sp = unsafe.Pointer(prevDefer.sp)
	}
	systemstack(func() {
		gentraceback(pc, uintptr(sp), 0, gp, 0, nil, 0x7fffffff,
			func(frame *stkframe, unused unsafe.Pointer) bool {
				if prevDefer != nil && prevDefer.sp == frame.sp {
					// 跳过上一个延迟的帧
					// 我们刚刚完成（用于设置
					// 我们重新启动堆栈扫描的位置）
					return true
				}
				f := frame.fn
				fd := funcdata(f, _FUNCDATA_OpenCodedDeferInfo)
				if fd == nil {
					return true
				}
				// 在
				// 链中插入打开的延迟记录，按sp排序。
				d := gp._defer
				var prev *_defer
				for d != nil {
					dsp := d.sp
					if frame.sp < dsp {
						break
					}
					if frame.sp == dsp {
						if !d.openDefer {
							throw("duplicated defer entry")
						}
						// 不要在
						// 正在进行的延迟条目之后添加任何记录。我们不需要它，更重要的是，我们想要保持不变，即
						// 没有打开的延迟条目
						// 传递了一个正在进行的条目（请参见
						// 标题注释）。
						if d.started {
							return false
						}
						return true
					}
					prev = d
					d = d.link
				}
				if frame.fn.deferreturn == 0 {
					throw("missing deferreturn")
				}

				d1 := newdefer()
				d1.openDefer = true
				d1._panic = nil
				// 这些是在我们完成
				// 运行延迟后要设置的pc/sp，该延迟在此帧中执行了
				// 恢复。我们返回一个特殊的
				// deferreturn，它运行任何剩余的
				// defers，然后从
				// 函数返回。
				d1.pc = frame.fn.entry() + uintptr(frame.fn.deferreturn)
				d1.varp = frame.varp
				d1.fd = fd
				// 保存与当前帧关联的SP/PC，
				// 以便我们可以在需要时继续堆栈跟踪。
				d1.framepc = frame.pc
				d1.sp = frame.sp
				d1.link = d
				if prev == nil {
					gp._defer = d1
				} else {
					prev.link = d1
				}
				// 添加一条打开的延迟记录后停止堆栈扫描
				return false
			},
			nil, 0)
	})
}

// ReadVarintSafe从fd开始以varint格式读取uint32，并返回
// uint32和指向varint后面字节的指针。
// 
// 有一个类似的函数运行时。readvarint需要一个字节片，
// 而不是一个不安全的指针。这些功能是重复的，因为
// 如果将这两个函数合并在一起，那么这两个函数的使用情况会变慢。
func readvarintUnsafe(fd unsafe.Pointer) (uint32, unsafe.Pointer) {
	var r uint32
	var shift int
	for {
		b := *(*uint8)((unsafe.Pointer(fd)))
		fd = add(fd, unsafe.Sizeof(b))
		if b < 128 {
			return r + uint32(b)<<shift, fd
		}
		r += ((uint32(b) &^ 128) << shift)
		shift += 7
		if shift > 28 {
			panic("Bad varint")
		}
	}
}

// RunoPendeFrame在
// d指定的帧中运行活动的开放编码延迟。它通常处理该帧中的所有活动延迟，但如果延迟成功恢复，它会立即停止
// 。如果帧中没有
// 要运行的剩余延迟，则返回true。
func runOpenDeferFrame(gp *g, d *_defer) bool {
	done := true
	fd := d.fd

	deferBitsOffset, fd := readvarintUnsafe(fd)
	nDefers, fd := readvarintUnsafe(fd)
	deferBits := *(*uint8)(unsafe.Pointer(d.varp - uintptr(deferBitsOffset)))

	for i := int(nDefers) - 1; i >= 0; i-- {
		// 读取此延迟的funcdata信息
		var closureOffset uint32
		closureOffset, fd = readvarintUnsafe(fd)
		if deferBits&(1<<i) == 0 {
			continue
		}
		closure := *(*func())(unsafe.Pointer(d.varp - uintptr(closureOffset)))
		d.fn = closure
		deferBits = deferBits &^ (1 << i)
		*(*uint8)(unsafe.Pointer(d.varp - uintptr(deferBitsOffset))) = deferBits
		p := d._panic
		// 调用延迟。请注意，如果堆栈移动，这可能会改变d.varp。
		deferCallSave(p, d.fn)
		if p != nil && p.aborted {
			break
		}
		d.fn = nil
		if d._panic != nil && d._panic.recovered {
			done = deferBits == 0
			break
		}
	}

	return done
}

// 在
// 紧急记录中保存呼叫者的pc和sp后，延迟CallSave调用fn（）。这允许运行时返回Goexit defer 
// 处理循环，在Goexit可能被成功恢复绕过的异常情况下。
// 
// 编译器将其标记为包装器，因此它不会出现在
// 回溯中。
func deferCallSave(p *_panic, fn func()) {
	if p != nil {
		p.argp = unsafe.Pointer(getargp())
		p.pc = getcallerpc()
		p.sp = unsafe.Pointer(getcallersp())
	}
	fn()
	if p != nil {
		p.pc = 0
		p.sp = unsafe.Pointer(nil)
	}
}

// 预声明函数的实现。
func gopanic(e any) {
	gp := getg()
	if gp.m.curg != gp {
		print("panic: ")
		printany(e)
		print("\n")
		throw("panic on system stack")
	}

	if gp.m.mallocing != 0 {
		print("panic: ")
		printany(e)
		print("\n")
		throw("panic during malloc")
	}
	if gp.m.preemptoff != "" {
		print("panic: ")
		printany(e)
		print("\n")
		print("preempt off reason: ")
		print(gp.m.preemptoff)
		print("\n")
		throw("panic during preemptoff")
	}
	if gp.m.locks != 0 {
		print("panic: ")
		printany(e)
		print("\n")
		throw("panic holding locks")
	}

	var p _panic
	p.arg = e
	p.link = gp._panic
	gp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))

	atomic.Xadd(&runningPanicDefers, 1)

	// 通过在这里计算getcallerpc/getcallersp，我们避免了扫描
	// gopanic帧（堆栈扫描很慢…）
	addOneOpenDeferFrame(gp, getcallerpc(), unsafe.Pointer(getcallersp()))

	for {
		d := gp._defer
		if d == nil {
			break
		}

		// 如果延迟是由之前的恐慌或Goexit启动的（既然我们回到这里，这引发了新的恐慌），
		// 将延迟从列表中删除。早些时候的恐慌不会继续，但我们将确保在以下情况下，
		// 早些时候的Goexit确实会继续运行。
		if d.started {
			if d._panic != nil {
				d._panic.aborted = true
			}
			d._panic = nil
			if !d.openDefer {
				// 对于开放编码延迟，我们需要再次处理
				// 延迟，以防帧中有任何其他延迟
				// 调用（不包括导致恐慌的延迟
				// 调用）。
				d.fn = nil
				gp._defer = d.link
				freedefer(d)
				continue
			}
		}

		// 将defer标记为started，但保留在列表中，以便在堆栈增长
		// 可以找到并更新defer的参数帧。
		// 或执行d.fn之前发生垃圾收集时，回溯
		d.started = true

		// 记录运行延迟的恐慌。
		// 如果在延迟通话期间出现新的恐慌，该恐慌
		// 将在列表中找到d并标记为d。_恐慌（此恐慌）中止。
		d._panic = (*_panic)(noescape(unsafe.Pointer(&p)))

		done := true
		if d.openDefer {
			done = runOpenDeferFrame(gp, d)
			if done && !d._panic.recovered {
				addOneOpenDeferFrame(gp, 0, nil)
			}
		} else {
			p.argp = unsafe.Pointer(getargp())
			d.fn()
		}
		p.argp = nil

		// 延迟函数没有死机。删除d.
		if gp._defer != d {
			throw("bad defer entry in panic")
		}
		d._panic = nil

		// 触发收缩以测试堆栈副本。参见stack_测试。go:TestStackPanic 
		// GC（）

		pc := d.pc
		sp := unsafe.Pointer(d.sp) // 必须是指针，以便在堆栈复制期间对其进行调整
		if done {
			d.fn = nil
			gp._defer = d.link
			freedefer(d)
		}
		if p.recovered {
			gp._panic = p.link
			if gp._panic != nil && gp._panic.goexit && gp._panic.aborted {
				// 正常恢复将绕过/中止Goexit。相反，我们回到Goexit的处理循环。
				gp.sigcode0 = uintptr(gp._panic.sp)
				gp.sigcode1 = uintptr(gp._panic.pc)
				mcall(recovery)
				throw("bypassed recovery failed") // mcall不应返回
			}
			atomic.Xadd(&runningPanicDefers, -1)

			// 恢复后，请删除任何剩余的未启动、
			// 打开的编码延迟项，因为相应的延迟
			// 将正常执行（内联）。一旦我们运行相应的延迟内联
			// 并退出相关的堆栈帧，任何这样的条目都将过时。我们只删除最多
			// 第一个已启动（正在进行）的open defer条目，而不是
			// 包括当前帧，因为任何更高的条目都将
			// 处于更高的恐慌中，并且仍然需要
			// 。
			d := gp._defer
			var prev *_defer
			if !done {
				// 如果未完成，请跳过当前帧。它是
				prev = d
				d = d.link
			}
			for d != nil {
				if d.started {
					// 进一步延迟条目
					break
				}
				if d.openDefer {
					if prev == nil {
						gp._defer = d.link
					} else {
						prev.link = d.link
					}
					newd := d.link
					freedefer(d)
					d = newd
				} else {
					prev = d
					d = d.link
				}
			}

			gp._panic = p.link
			// 标记中止的恐慌，但仍保留在g.恐慌列表中。
			// 将其从列表中删除。
			for gp._panic != nil && gp._panic.aborted {
				gp._panic = gp._panic.link
			}
			if gp._panic == nil { // 必须使用信号
				gp.sig = 0
			}
			// 将有关恢复帧的信息传递给恢复。
			gp.sigcode0 = uintptr(sp)
			gp.sigcode1 = pc
			mcall(recovery)
			throw("recovery failed") // McCall不应返回
		}
	}

	// 延迟调用用完了-老派恐慌现在
	// 因为冻结后调用任意用户代码是不安全的
	// 世界，我们调用预打印程序调用所有必要的错误
	// 和字符串方法在启动前准备恐慌字符串。
	preprintpanics(gp._panic)

	fatalpanic(gp._panic) // 不应返回
	*(*int)(nil) = 0      // 未到达
}

// getargp返回调用方
// 写入传出函数调用参数的位置。
// go:nosplit 
// go:noinline 
func getargp() uintptr {
	return getcallersp() + sys.MinFrameSize
}

// 预声明函数recover的实现。
// 无法拆分堆栈，因为它需要可靠地找到调用方的堆栈段。
// 
// TODO（rsc）：一旦我们承诺CopyStackAlways，
// 这不需要是nosplit。
// go:nosplit 
func gorecover(argp uintptr) any {
	// 在紧急状态下，必须在作为延迟调用的一部分运行的函数中。
	// 必须从调用
	// （在DEBER语句中使用的函数）的最顶层函数调用。
	// p.argp是最上面的延迟函数调用的参数指针。
	// 与调用者报告的argp进行比较。
	// 如果匹配，则呼叫方是可以恢复的人。
	gp := getg()
	p := gp._panic
	if p != nil && !p.goexit && !p.recovered && argp == uintptr(p.argp) {
		p.recovered = true
		return p.arg
	}
	return nil
}

// go:linkname-sync\u-throw-sync。throw-
func sync_throw(s string) {
	throw(s)
}

// go:nosplit-
func throw(s string) {
	// throw所做的一切都应该是递归的nosplit，这样即使在堆栈增长不安全的情况下也可以调用它。
	systemstack(func() {
		print("fatal error: ", s, "\n")
	})
	gp := getg()
	if gp.m.throwing == 0 {
		gp.m.throwing = 1
	}
	fatalthrow()
	*(*int)(nil) = 0 // 未达到
}

// 在为恐慌运行延迟函数时，RunningPanicDeferres为非零。
// runningPanicDefers是以原子方式递增和递减的。
// 这用于在退出时努力获取紧急堆栈跟踪。
var runningPanicDefers uint32

// 当程序因未恢复的恐慌而崩溃时，恐慌不为零。
// 恐慌是原子级递增和递减的。
var panicking uint32

// 在打印紧急信息和堆栈跟踪时保持paniclk，
// 这样两个并发的panick不会重叠它们的输出。
var paniclk mutex

// 在延迟的函数调用后解除堆栈恢复
// 在恐慌之后。然后安排继续运行，就像
// 延迟函数的调用方正常返回一样。
func recovery(gp *g) {
	// 有关在G结构中传递的延迟的信息。
	sp := gp.sigcode0
	pc := gp.sigcode1

	// d的参数必须在堆栈中。
	if sp != 0 && (sp < gp.stack.lo || gp.stack.hi < sp) {
		print("recover: ", hex(sp), " not in [", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n")
		throw("bad recovery")
	}

	// 再次执行此d返回的延迟过程，
	// 这次返回1。调用函数将跳转到标准返回尾声。
	gp.sched.sp = sp
	gp.sched.pc = pc
	gp.sched.lr = 0
	gp.sched.ret = 1
	gogo(&gp.sched)
}

// fatalthrow实现一个不可恢复的运行时抛出。它冻结
// 系统，打印从调用方开始的堆栈跟踪，并终止
// 进程。
// 
// go:nosplit 
func fatalthrow() {
	pc := getcallerpc()
	sp := getcallersp()
	gp := getg()
	// 切换到系统堆栈以避免任何堆栈增长，如果运行时处于不良状态，这可能会使情况变得更糟。
	systemstack(func() {
		startpanic_m()

		if dopanic_m(gp, pc, sp) {
			// crash使用了相当多的nosplit堆栈，我们已经在堆栈抛出中处于低位，因此在系统堆栈上崩溃（与
			// fatalpanic不同）。
			crash()
		}

		exit(2)
	})

	*(*int)(nil) = 0 // 未联系到
}

// 法塔尔潘实施了无法恢复的恐慌。除了
// 如果MSG！=无，fatalpanic也会打印恐慌信息，并在main被阻止退出后减少
// 运行恐慌延迟。
// 
// go:nosplit 
func fatalpanic(msgs *_panic) {
	pc := getcallerpc()
	sp := getcallersp()
	gp := getg()
	var docrash bool
	// 切换到系统堆栈以避免任何堆栈增长，如果运行时处于不良状态，这可能会使情况变得更糟。
	systemstack(func() {
		if startpanic_m() && msgs != nil {
			// 有恐慌信息，startpanic_m 
			// 表示可以尝试打印这些信息。

			// startpanic_m set恐慌，这将阻止main退出，现在确定
			// DECRIMENT RUNNINGPANINGDEERES。
			atomic.Xadd(&runningPanicDefers, -1)

			printpanics(msgs)
		}

		docrash = dopanic_m(gp, pc, sp)
	})

	if docrash {
		// 通过在上述systemstack调用之外崩溃，调试器
		// 在生成回溯跟踪时不会混淆。
		// 函数崩溃被标记为nosplit以避免堆栈增长。
		crash()
	}

	systemstack(func() {
		exit(2)
	})

	*(*int)(nil) = 0 // 未联系到
}

// startpanic_m为无法恢复的恐慌做好准备。
// 
// 如果应该打印紧急消息，则返回true；如果
// 则返回false。运行时状态不好，应该只打印堆栈。
// 
// 即使写屏障
// 在死后显式忽略写入>0，也不能有写屏障。写障碍仍然
// 假设g.m.p！=nil，并且在某些上下文中，此函数可能没有P 
// 值（例如，信号处理程序中的一个死机，将信号
// 发送到没有P的M）。
// 
// go:nowritebarrierrec 
func startpanic_m() bool {
	_g_ := getg()
	if mheap_.cachealloc.size == 0 { // 非常早的
		print("runtime: panic before malloc heap initialized\n")
	}
	// 在无法恢复的恐慌中不允许马洛克。恐慌可能发生在信号处理程序中，或在抛出过程中，或在
	// malloc本身内部。我们想知道是否发生过分配
	// 的情况（即使我们没有遇到这种情况）。
	_g_.m.mallocing++

	// 如果我们因为错误的锁计数而死亡，请将其设置为
	// 正确的锁计数，这样我们就不会在下面递归地恐慌。
	if _g_.m.locks < 0 {
		_g_.m.locks = 1
	}

	switch _g_.m.dying {
	case 0:
		// 设置DINGE>0会产生禁用此G的writebuf的副作用。
		_g_.m.dying = 1
		atomic.Xadd(&panicking, 1)
		lock(&paniclk)
		if debug.schedtrace > 0 || debug.scheddetail > 0 {
			schedtrace(true)
		}
		freezetheworld()
		return true
	case 1:
		// 惊慌失措时，有些事情失败了。
		// 只需打印堆栈跟踪并退出即可。
		_g_.m.dying = 2
		print("panic during panic\n")
		return false
	case 2:
		// 这是运行时的一个真正错误，我们甚至无法成功打印堆栈跟踪。
		_g_.m.dying = 3
		print("stack trace unavailable\n")
		exit(4)
		fallthrough
	default:
		// 甚至无法打印！退出。
		exit(5)
		return false // 需要退货。
	}
}

var didothers bool
var deadlock mutex

func dopanic_m(gp *g, pc, sp uintptr) bool {
	if gp.sig != 0 {
		signame := signame(gp.sig)
		if signame != "" {
			print("[signal ", signame)
		} else {
			print("[signal ", hex(gp.sig))
		}
		print(" code=", hex(gp.sigcode0), " addr=", hex(gp.sigcode1), " pc=", hex(gp.sigpc), "]\n")
	}

	level, all, docrash := gotraceback()
	_g_ := getg()
	if level > 0 {
		if gp != gp.m.curg {
			all = true
		}
		if gp != gp.m.g0 {
			print("\n")
			goroutineheader(gp)
			traceback(pc, sp, 0, gp)
		} else if level >= 2 || _g_.m.throwing > 0 {
			print("\nruntime stack:\n")
			traceback(pc, sp, 0, gp)
		}
		if !didothers && all {
			didothers = true
			tracebackothers(gp)
		}
	}
	unlock(&paniclk)

	if atomic.Xadd(&panicking, -1) != 0 {
		// 其他一些m也在恐慌。
		// 让它打印需要打印的内容。
		// 永远等待，不要占用cpu。
		// 完成后将退出。
		lock(&deadlock)
		lock(&deadlock)
	}

	printDebugLog()

	return docrash
}

// 如果信号应该抛出，而不是
// 惊慌失措，则canpanic返回false。
// 
// go:nosplit 
func canpanic(gp *g) bool {
	// 注意g是m->gsignal，不同于gp。
	// 请注意，g->m可以在抢占时更改，因此如果此函数调用函数，m可能会过时。
	_g_ := getg()
	mp := _g_.m

	// gp恐慌而不是破坏程序可以吗？
	// 是的，只要它运行的是Go代码，而不是运行时代码，
	// 并且没有陷入系统调用。
	if gp == nil || gp != mp.curg {
		return false
	}
	if mp.locks != 0 || mp.mallocing != 0 || mp.throwing != 0 || mp.preemptoff != "" || mp.dying != 0 {
		return false
	}
	status := readgstatus(gp)
	if status&^_Gscan != _Grunning || gp.syscallsp != 0 {
		return false
	}
	if GOOS == "windows" && mp.libcallsp != 0 {
		return false
	}
	return true
}

// shouldPushSigpanic报告pc是否应被用作Sigmanic的
// return pc（为通话推帧）。否则，它应该是
// 单独使用，以便LR被用作sigpanic的返回PC，实际上
// 用sigpanic替换最上面的帧。这是
// preparePanic使用的。
func shouldPushSigpanic(gp *g, pc, lr uintptr) bool {
	if pc == 0 {
		// 可能是对nil func的调用。旧的LR在堆栈跟踪中更有用。不按帧
		// 将使跟踪看起来像是对SIGMANIC 
		// 的调用。（否则，追踪将以sigpanic 
		// 结束，我们将无法看到谁犯了错。）
		return false
	}
	// 如果我们不将PC识别为代码，但我们确实识别了
	// 将链接寄存器识别为代码，那么这就假设恐慌是由非代码调用引起的。在本例中，我们希望
	// 忽略此调用以使展开显示上下文。
	// 
	// 如果我们运行C代码，我们不会将pc识别为
	// Go函数，所以假设它是好的。否则，回溯
	// 可能会尝试读取一个看起来像围棋代码
	// 指针的陈旧LR，然后漫步到树林中。
	if gp.m.incgo || findfunc(pc).valid() {
		// 这是一个不错的电话，所以使用PC作为Sigmanic的返回PC。
		return true
	}
	if findfunc(lr).valid() {
		// 这是一个糟糕的电话，但LR是好的，所以使用
		// LR作为Sigmanic的返回PC。
		return false
	}
	// PC或LR都不好。希望推动
	// /的框架能奏效。
	return true
}

// isAbortPC报告pc是否是运行时
// 的程序计数器。中止发出信号。
// 
// 它是不完整的，因为它是isgoexception 
// 实现的一部分。
// 
// go:nosplit 
func isAbortPC(pc uintptr) bool {
	f := findfunc(pc)
	if !f.valid() {
		return false
	}
	return f.funcID == funcID_abort
}
